/*
 *  Copyright 2008-2010 www.jrails.org
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.rails.core.web;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.log4j.Logger;

import org.rails.core.GLOBAL;
import org.rails.core.util.Utils;

/**
 * 控制器基类,所有控制器必须继承此类并放到app.controller包下,并且命名必须为 [Contro Name]Controller.java 格式。
 * @author jack
 *
 */
public abstract class ControllerBase {

	protected final Logger logger = Logger.getLogger(this.getClass());
	
	protected final Map<String, Object> params = new HashMap<String, Object>();
	protected final ServletFileUpload upload = new ServletFileUpload();
	
	protected Action action;
	protected HttpServletRequest request;
	protected HttpServletResponse response;
	protected ServletConfig config;
	protected Map<String, String[]> paramsMap;
	protected boolean isGet = false;

	@SuppressWarnings("unchecked")
	public ControllerBase(Action action) throws Exception {
		super();
		this.action = action;
		this.request = this.action.getRequest();
		this.response = this.action.getResponse();
		
		isGet = this.request.getMethod().toUpperCase().equals("GET") ? true
				: false;

		if (ServletFileUpload.isMultipartContent(request)) {			
			parseDataForm();
		} else {
			this.paramsMap = request.getParameterMap();
			parseSimpleForm();			
		}
	}

	/**
	 * 解释非二进制数据表单提交表单。
	 * @throws UnsupportedEncodingException
	 */
	@SuppressWarnings("unchecked")
	protected void parseSimpleForm() throws UnsupportedEncodingException {
		final List<String> keys = Utils.getKeys(paramsMap);
		for (Iterator<String> iterator = keys.iterator(); iterator.hasNext();) {
			final String key = iterator.next();
			params.put(key, parseStringArray(paramsMap.get(key)));
		}
	}

	/**
	 * 解释二进制数据表单提交表单。
	 * @throws FileUploadException
	 * @throws IOException
	 */
	protected void parseDataForm() throws FileUploadException, IOException {
		final FileItemIterator terator = upload.getItemIterator(request);
		while (terator.hasNext()) {
			final FileItemStream item = terator.next();
			final String fieldName = item.getFieldName();
			if (item.isFormField()) {
				parseSimpleField(fieldName, item);
			} else {
				parseFileField(item);
			}
		}
	}

	@SuppressWarnings("unchecked")
	private void parseSimpleField(String fieldName, FileItemStream item)
			throws IOException {
		final InputStream stream = item.openStream();
		final String value = Streams.asString(stream,GLOBAL.REQUEST_CHARSET_NAME);
		if (params.containsKey(fieldName)) {
			final Object obj = params.get(fieldName);
			List<String> list = null;
			if (obj instanceof String) {
				list = new ArrayList<String>();
				list.add((String) obj);
			} else {
				list = (List<String>) obj;
			}
			list.add(value);
			params.put(fieldName, list);
		} else
			params.put(fieldName, value);
	}

	@SuppressWarnings("unchecked")
	private void parseFileField(FileItemStream item)
			throws IOException {
		if (params.containsKey(item.getFieldName())) {
			final Object obj = params.get(item.getFieldName());
			List<ClientFile> list = null;
			if (obj instanceof FileItemStream) {
				list = new ArrayList<ClientFile>();
				list.add(toClientFile((FileItemStream) obj));
			}else if(obj instanceof ClientFile){ 
				list = new ArrayList<ClientFile>();
				list.add((ClientFile)obj);
			}else {
				list = (List<ClientFile>) obj;
			}
			list.add(toClientFile(item));
			params.put(item.getFieldName(), list);
		} else
			params.put(item.getFieldName(), toClientFile(item));
	}

	private ClientFile toClientFile(FileItemStream item) throws IOException {
		final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
		InputStream is = null;;
		int ch;
		try {
			is = item.openStream();
			while ((ch = is.read()) != -1) {
				byteArray.write(ch);
			}
			return new ClientFile(item.getContentType(), item.getFieldName(),
					item.getName(), byteArray.toByteArray());
		} finally {
			if(is != null)
				is.close();
			byteArray.close();
		}
	}

	private Object parseStringArray(String[] arrays) {
		if (arrays.length == 1) {
			return encodeParam(arrays[0]);
		} else {
			List<String> arr = new ArrayList<String>();
			for (int i = 0; i < arrays.length; i++) {
				arr.add(encodeParam(arrays[i]));
			}
			return arr;
		}
	}

	
	@SuppressWarnings("unchecked")
	private <T extends Map<String,Object>> T parseClientForm(T record,
			Map<String, Object> params) {
		final List<String> paramKeys = Utils.getKeys(params);
		for (Iterator<String> iterator = paramKeys.iterator(); iterator
				.hasNext();) {
			String key = iterator.next();
			String regex = Utils.format("^{0}[\\.]{1}", record.getClass()
					.getSimpleName());
			Pattern p = Pattern.compile(regex);
			Matcher mat = p.matcher(key);
			if (mat.find()) {
				record.put(key.replaceFirst(regex, ""), params.get(key));
			}
		}
		return record;
	}

	/**
	 * 解释客户端表单参数,转换为提供的Map<String,Object>类型。
	 * @param <T> ActiveRecord类型。
	 * @param record ActiveRecord对象。
	 * @return ActiveRecord对象。
	 */
	protected <T extends Map<String,Object>> T parseClientForm(T record) {
		return parseClientForm(record, params);
	}

	/**
	 * 重写此方法可实现处理乱码问题。
	 * @param value 表单字段值。
	 * @return 处理后的字符串。
	 */
	protected String encodeParam(String value) {
		return value;
	}

}
